using Cairo;
using Gtk;

namespace Ribbons {

	/** Button to be used in Ribbons. */
	public class Button : BaseButton {

		private double _arrow_size;
		private Gdk.Rectangle _arrow_allocation;

		protected const double LINE_WIDTH = 1.0;
		protected const double ARROW_PADDING = 2.0;
		protected const double SMALL_ARROW_SIZE = 5.0;
		protected const double BIG_ARROW_SIZE = 8.0;

		/** Fired when the button is clicked. */
		public signal void clicked ();

		/** Drop down menu displayed when the arrow is pressed. */
		private Menu _drop_down_menu;
		public Menu drop_down_menu {
			set {
				_drop_down_menu = value;
				queue_draw ();
			}
			get {
				return _drop_down_menu;
			}
		}

		/** Construction method. */
		construct {
			set_flags (get_flags () | WidgetFlags.NO_WINDOW);

			add_events (Gdk.EventMask.BUTTON_PRESS_MASK
			          | Gdk.EventMask.BUTTON_RELEASE_MASK
			          | Gdk.EventMask.POINTER_MOTION_MASK);

			this.padding = 2;
			this.image_position = PositionType.TOP;
			_is_small = false;
			_enabled = true;
		}

		/**
		 * Constructor given a label to display.
		 *
		 * @param label  Label to display.
		 */
		public Button.with_label (string label) {
			this.label = label;
		}

		/**
		 * Constructor given an image to display.
		 *
		 * @param Image  Image to display
		 */
		public Button.with_image (Image image) {
			this.image = image;
		}

		/**
		 * Constructor given a label and an image to display.
		 *
		 * @param image  Image to display.
		 * @param label  label to display.
		 */
		public Button (Image image, string label) {
			this.image = image;
			this.label = label;
		}

		/**
		 * Constructs a Button from a stock.
		 *
		 * @param name   Name of the stock.
		 * @param large  <b>true</b> if the image should be large,
		 *               <b>false</b> otherwise.
		 */
		public static Button from_stock (string name, bool large) {
			var img = new Image.from_stock (name, large ? IconSize.LARGE_TOOLBAR
			                                            : IconSize.SMALL_TOOLBAR);
			var btn = new Button.with_image (img);
			if (!large) {
				btn.image_position = PositionType.LEFT;
			}
			return btn;
		}

		/**
		 * Constructs a Button from a stock.
		 *
		 * @param name   Name of the stock.
		 * @param label  label to display.
		 * @param large  <b>true</b> if the image should be large,
		 *               <b>false</b> otherwise.
		 */
		public static Button from_stock_with_label (string name, string label, bool large) {
			var img = new Image.from_stock (name, large ? IconSize.LARGE_TOOLBAR
			                                            : IconSize.SMALL_TOOLBAR);
			var btn = new Button (img, label);
			if (!large) {
				btn.image_position = PositionType.LEFT;
			}
			return btn;
		}

		/** Fires the clicked event. */
		private void click () {
			if (_enabled) {
				clicked ();
			}
		}

		// XXX-GEETK: workaround
		private bool has_click_listener () {
			var signal_id = Signal.lookup ("clicked", typeof (Button));
			return Signal.has_handler_pending (this, signal_id, 0, true);
		}

		/** Displays the drop down menu if any. */
		public void popup () {
			if (_enabled && _drop_down_menu != null) {
				_drop_down_menu.popup (null, null, null, 3, Gtk.get_current_event_time ());
				_drop_down_menu.show_all ();
			}
		}

		protected override bool bound_widget_button_press_event (Widget sender,
		                                                Gdk.EventButton evnt) {
			button_press_event (evnt);
			return false;
		}

		protected override bool bound_widget_button_release_event (Widget sender,
		                                                Gdk.EventButton evnt) {
			button_release_event (evnt);
			click ();
			return false;
		}

		protected override void size_request (out Requisition requisition) {
			base.size_request (out requisition);
			
			Requisition child_requisition = Requisition ();
			if (this.child != null && this.child.visible) {
				 this.child.size_request (out child_requisition);
			}

			if (_drop_down_menu != null) {
				int arrow_space = (int) ((_is_small ? SMALL_ARROW_SIZE : BIG_ARROW_SIZE)
				                        + 2 * (LINE_WIDTH + ARROW_PADDING));

				if (_image_position == PositionType.TOP
				            || _image_position == PositionType.BOTTOM) {
					child_requisition.height += arrow_space;
				} else {
					child_requisition.width += arrow_space;
				}
			}

			if (this.height_request == -1) {
				requisition.height = child_requisition.height
				                        + (int) (LINE_WIDTH * 4 + _padding * 2);
			}
			if (this.width_request == -1) {
				requisition.width = child_requisition.width
				                        + (int) (LINE_WIDTH * 4 + _padding * 2);
			}
		}

		protected override void size_allocate (Gdk.Rectangle alloc) {
			base.size_allocate (alloc);
			Gdk.Rectangle allocation = alloc;	// XXX-GEETK

			if (_drop_down_menu != null) {
				_arrow_size = _is_small ? SMALL_ARROW_SIZE : BIG_ARROW_SIZE;

				if (_image_position == PositionType.TOP
							|| _image_position == PositionType.BOTTOM) {
					if (has_click_listener ()) {
						_arrow_allocation.height = (int) (_arrow_size + 2 * ARROW_PADDING);
					} else {
						_arrow_allocation.height = (int) (allocation.height - 4 * LINE_WIDTH);
					}

					_arrow_allocation.width = (int) (allocation.width - 4 * LINE_WIDTH);
				} else {
					if (has_click_listener ()) {
						_arrow_allocation.width = (int) (_arrow_size + 2 * ARROW_PADDING);
					} else {
						_arrow_allocation.width = (int) (allocation.width - 4 * LINE_WIDTH);
					}

					_arrow_allocation.height = (int) (allocation.height - 4 * LINE_WIDTH);
				}

				_arrow_allocation.x = (int) (Gdk.Rect.right (allocation)
												- _arrow_allocation.width
												- 2 * LINE_WIDTH);
				_arrow_allocation.y = (int) (Gdk.Rect.bottom (allocation)
												- _arrow_allocation.height
												- 2 * LINE_WIDTH);
			} else {
				_arrow_size = 0;
			}

			allocation.x += (int) (LINE_WIDTH * 2 + _padding);
			allocation.y += (int) (LINE_WIDTH * 2 + _padding);
			allocation.height -= (int) (LINE_WIDTH * 4 + _padding * 2);
			allocation.width -= (int) (LINE_WIDTH * 4 + _padding * 2);

			if (_drop_down_menu != null) {
				int arrow_space = (int) ((_is_small ? SMALL_ARROW_SIZE : BIG_ARROW_SIZE)
									+ 2 * (LINE_WIDTH + ARROW_PADDING));

				if (_image_position == PositionType.TOP
							|| _image_position == PositionType.BOTTOM) {
					allocation.height -= arrow_space;
				} else {
					allocation.width -= arrow_space;
				}
			}

			if (allocation.height < 0) {
				allocation.height = 0;
			}
			if (allocation.width < 0) {
				allocation.width = 0;
			}

			if (this.child != null && this.child.visible) {
				this.child.size_allocate (allocation);
			}
		}

		protected override bool expose_event (Gdk.EventExpose event) {
			var cr = Gdk.cairo_create (this.window);

			cr.rectangle (event.area.x, event.area.y,
			              event.area.width, event.area.height);
			cr.clip ();
			draw (cr);

//			cr.target.dispose ();	// XXX-GEETK
//			cr.dispose ();

			return base.expose_event (event);
		}

		protected void draw (Context cr) {
			Gdk.Rectangle rect = (Gdk.Rectangle) this.allocation;
			double round_size = _is_small ? 2.0 : 3.0;
			bool draw_separator = (has_click_listener ()) && (_drop_down_menu != null);
			_theme.draw_button (cr, rect, _state, round_size, LINE_WIDTH,
			                    _arrow_size, ARROW_PADDING, draw_separator, this);
		}

		protected override bool button_press_event (Gdk.EventButton event) {
//			bool ret = base.button_press_event (event);		// XXX-GEETK
			bool ret = false;
			_state = Theme.ButtonState.PRESSED;
			if (!_enabled) {
				_state = Theme.ButtonState.DEFAULT;
			}
			queue_draw ();

			if (_drop_down_menu != null
			        && Gdk.Rect.contains (_arrow_allocation, (int) event.x, (int) event.y)) {
				popup ();
			}

			return ret;
		}

		protected override bool button_release_event (Gdk.EventButton event) {
//			bool ret = base.button_release_event (event);	// XXX-GEETK
			bool ret = false;
			_state = Theme.ButtonState.HOVER;
			if (!_enabled) {
				_state = Theme.ButtonState.DEFAULT;
			}
			queue_draw ();
			return ret;
		}

		protected override bool enter_notify_event (Gdk.EventCrossing event) {
//			bool ret = base.enter_notify_event (event);		// XXX-GEETK
			bool ret = false;
			_state = Theme.ButtonState.HOVER;
			if (!_enabled) {
				_state = Theme.ButtonState.DEFAULT;
			}
			queue_draw ();
			return ret;
		}

		protected override bool leave_notify_event (Gdk.EventCrossing event) {
//			bool ret = base.leave_notify_event (event);		// XXX-GEETK
			bool ret = false;
			_state = Theme.ButtonState.DEFAULT;
			queue_draw ();
			return ret;
		}
	}
}
